﻿using System;
using System.Collections.Generic;
using Firefly.ExtensionMethods;
using Firefly.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Firefly.Framework.Input
{
    public class MouseManager : HumanInterfaceDevice<MouseState>, IHumanInterfaceDevice<MouseState>
    {
        public bool ResetAfterUpdate { get; set; }
        public List<MouseButton> CurrentButtonsPressed { get; private set; }
        public List<MouseButton> PreviousButtonsPressed { get; private set; }
        public Vector2 CurrentAxisPosition
        {
            get { return new Vector2(CurrentState.X, CurrentState.Y); }
        }
        public Vector2 PreviousAxisPosition
        {
            get { return new Vector2(PreviousState.X, PreviousState.Y); }
        }
        public Vector2 AxisDelta
        {
            get
            {
                if (ResetAfterUpdate)
                {
                    Vector2 center = new Vector2(GraphicsService.Instance.Viewport.Width / 2, GraphicsService.Instance.Viewport.Height / 2);
                    return new Vector2(CurrentAxisPosition.X - center.X, CurrentAxisPosition.Y - center.Y);                    
                }
                else
                {
                    return new Vector2(CurrentAxisPosition.X - PreviousState.X, CurrentAxisPosition.Y - PreviousState.Y);
                }
            }
        }
        public int CurrentScrollWheelPosition
        {
            get { return CurrentState.ScrollWheelValue; }
        }
        public int PreviousScrollWheelPosition
        {
            get { return PreviousState.ScrollWheelValue; }
        }
        public int ScrollWheelDelta
        {
            get { return CurrentState.ScrollWheelValue - PreviousState.ScrollWheelValue; }
        }
        public bool AxisPositionInViewport
        {
            get
            {
                Rectangle viewport = new Rectangle(0, 
                    0,
                    GraphicsService.Instance.Viewport.Width,
                    GraphicsService.Instance.Viewport.Height);
                return viewport.Contains(CurrentAxisPosition.ToPoint());
            }
        }

        public MouseManager()
        {
            DeviceType = DeviceType.Mouse;
            CurrentButtonsPressed = new List<MouseButton>();
            PreviousButtonsPressed = new List<MouseButton>();
            CurrentState = Mouse.GetState();
        }

        public bool IsButtonUp(MouseButton button)
        {
            return (ResolveButtonState(CurrentState, button) == ButtonState.Released) ? true : false;
        }

        public bool WasButtonUp(MouseButton button)
        {
            return (ResolveButtonState(PreviousState, button) == ButtonState.Released) ? true : false;
        }

        public bool IsButtonDown(MouseButton button)
        {
            return (ResolveButtonState(CurrentState, button) == ButtonState.Pressed) ? true : false;
        }

        public bool WasButtonDown(MouseButton button)
        {
            return (ResolveButtonState(PreviousState, button) == ButtonState.Pressed) ? true : false;
        }

        public bool WasButtonPressed(MouseButton button)
        {
            return (WasButtonUp(button) && IsButtonDown(button)) ? true : false;
        }

        public bool WasButtonReleased(MouseButton button)
        {
            return (WasButtonDown(button) && IsButtonUp(button)) ? true : false;
        }

        public bool WasButtonHeld(MouseButton button)
        {
            return (WasButtonDown(button) && IsButtonDown(button)) ? true : false;
        }

        private ButtonState ResolveButtonState(MouseState state, MouseButton button)
        {
            switch (button)
            {
                case MouseButton.LeftButton:
                    return state.LeftButton;
                case MouseButton.MiddleButton:
                    return state.MiddleButton;
                case MouseButton.RightButton:
                    return state.RightButton;
                case MouseButton.XButton1:
                    return state.XButton1;
                case MouseButton.XButton2:
                    return state.XButton2;
                default:
                    return ButtonState.Released;
            }
        }

        private void UpdateButtons()
        {
            PreviousButtonsPressed = CurrentButtonsPressed;
            CurrentButtonsPressed.Clear();

            foreach (MouseButton button in Enum.GetValues(typeof(MouseButton)))
            {
                if (ResolveButtonState(CurrentState, button) == ButtonState.Pressed)
                {
                    CurrentButtonsPressed.Add(button);
                }
            }
        }

        #region IHumanInterfaceDevice Members

        public override void RegisterInputMap(IInputMap map)
        {
            (map as IHasInputManager<MouseManager>).Manager = this;
            base.RegisterInputMap(map);
        }

        #endregion

        #region IUpdateable Members

        public override void Update(GameTime gameTime)
        {
            if (Enabled)
            {
                PreviousState = CurrentState;
                CurrentState = Mouse.GetState();
                UpdateButtons();

                if (ResetAfterUpdate)
                {
                    Mouse.SetPosition(GraphicsService.Instance.Viewport.Width / 2, GraphicsService.Instance.Viewport.Height / 2);
                }

                foreach (IInputMap map in InputMaps)
                {
                    if (map.InputMapType == InputMapType.MouseAxisMap || map.InputMapType == InputMapType.MouseButtonMap || map.InputMapType == InputMapType.MouseScrollWheelMap)
                    {
                        map.Update(gameTime);
                    }
                }

                if (CurrentState != PreviousState)
                {
                    NotifyStateChanged(new StateChangedEventArgs<MouseState>(CurrentState));
                }
            }
        }

        #endregion
    }
}
